package com.biwin.module.security.starter.filter;

import cn.hutool.core.util.StrUtil;
import com.biwin.core.main.property.BiWinLoginProperties;
import com.biwin.module.org.api.service.ISystemUserService;
import com.biwin.module.security.api.properties.BiWinSecurityProperties;
import com.biwin.module.security.api.vo.BwUserDetailVo;
import com.biwin.module.security.starter.exception.BiWinCaptchaCheckException;
import com.biwin.module.security.starter.exception.BiWinFirstLoginAuthenticationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

import static com.biwin.common.app.enums.BwSecurityExceptionEnum.*;

/**
 * BiWin Framework Custom UsernamePasswordAuthenticationFilter
 *
 * @author biwin
 * @since 0.0.1
 */
public class BwUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private static final Logger log = LoggerFactory.getLogger(BwUsernamePasswordAuthenticationFilter.class.getName());

    private final ISystemUserService systemUserService;

    private final BiWinSecurityProperties securityProperties;

    private final BiWinLoginProperties loginProperties;


    private boolean debugEnabled = false;

    public BwUsernamePasswordAuthenticationFilter(ISystemUserService systemUserService,
                                                  BiWinSecurityProperties securityProperties,
                                                  BiWinLoginProperties loginProperties,
                                                  SessionAuthenticationStrategy sessionAuthenticationStrategy) {
        debugEnabled = log.isDebugEnabled();
        this.systemUserService = systemUserService;
        this.securityProperties = securityProperties;
        this.loginProperties = loginProperties;
        this.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
        this.setFilterProcessesUrl(loginProperties.getCustomLoginUrl());
        this.setUsernameParameter(loginProperties.getUsername());
        this.setPasswordParameter(loginProperties.getPassword());
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String magicStr = "";
        // 开启了验证码
        if (securityProperties.getCaptcha().getEnable()) {
            magicStr = (String) request.getSession().getAttribute("captcha");
            if (StrUtil.isEmpty(magicStr)) {
                if (log.isErrorEnabled()) {
                    log.error(CAPTCHA_NOT_EXIST.getValue() + "[System]:系统未生成验证码!");
                }
                throw new BiWinCaptchaCheckException(CAPTCHA_NOT_EXIST);
            }
            String captchaCode = request.getParameter(loginProperties.getCaptcha());
            if (!StrUtil.equals(magicStr, captchaCode, true)) {
                if (log.isErrorEnabled()) {
                    log.error(CAPTCHA_ERROR.getValue() + "[System]: 验证码匹配失败!");
                }
                throw new BiWinCaptchaCheckException(CAPTCHA_ERROR);
            }
        }

        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (Objects.isNull(username) && Objects.isNull(password) && debugEnabled) {
            log.debug("无法获取 username 和 password 参数, 请确认是否在配置文件中修改过属性名称!");
        }

        if (Objects.nonNull(username)) {
            //校验登陆用户是否存在,如果存在则根据配置首次登陆是否强制修改密码进行处理.
            if (systemUserService.checkUserExist(username)) {
                Authentication authentication = super.attemptAuthentication(request, response);
                BwUserDetailVo user = (BwUserDetailVo) authentication.getPrincipal();
                if (securityProperties.getResetPasswordFirstLogin()
                        && Objects.isNull(user.getSystemUserVo().getPasswordLastModifyDate())) {
                    // todo 后续改为国际化
                    // 返回需要修改密码的提示
                    throw new BiWinFirstLoginAuthenticationException(USER_FIRST_LOGIN_NEED_RESET_PASSWORD);
                } else {
//                    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,
//                            password);
//                    setDetails(request, authRequest);
//                    return this.getAuthenticationManager().authenticate(authRequest);
                    return authentication;
                }
            }
        }
        // todo 后续改为国际化
        throw new InternalAuthenticationServiceException(USER_ACCOUNT_NOT_EXIST.getValue());

    }

    @Override
    public void setUsernameParameter(String usernameParameter) {
        if (debugEnabled && !usernameParameter.equals(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY)) {
            log.debug("检测到配置文件中存在重写登录用户属性名,新的用户属性名: [{}]", usernameParameter);
        }
        super.setUsernameParameter(loginProperties.getUsername());
    }

    @Override
    public void setPasswordParameter(String passwordParameter) {
        if (debugEnabled && !passwordParameter.equals(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY)) {
            log.debug("检测到配置文件中存在重写登录密码属性名,新的密码属性名: [{}]", passwordParameter);
        }
        super.setPasswordParameter(loginProperties.getPassword());
    }

    @Override
    public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {
        super.setSessionAuthenticationStrategy(sessionStrategy);
    }
}
